<?php
class RFParser {

    const DEFAULT_TAG = 'div';

    public $_templates = array();
    public $_blocks = array();
    public $_config = array(
    'ident'     => 4,
    'debug'     => false
    );

    public function load($name, $filename) {
        return $this->_templates[$name] = file($filename);
    }

    protected function _parse_template($name, &$pblock, $pident = 0,  &$line = 0) {
        $i = 0;
        $_lines = count($this->_templates[$name]);
        while ($line < $_lines) {
            $t = rtrim($this->_templates[$name][$line]);
            if (empty($t)) { $line++; continue; } // fast forward
            $m = array();
            $block = array();
            preg_match('~^(\s{0,})(.*)$~iu', $t, $m);
            $ident = strlen($m[1]) / $this->_config['ident'];
            if ($ident < $pident) {
            // echo "&lt;&lt; Уходим выше... (из $pident в $ident @ $line) [{$m[2]}]<br/>";
                return $ident;
            }
            if ($ident > $pident) {
            // echo "&gt;&gt; Уходим глубже... (из $pident в $ident % $i @ $line) [{$m[2]}]<br/>";
                $pblock[$i-1]['sub'] = array('ident'=>$ident);
                $ident = $this->_parse_template($name, $pblock[$i-1]['sub'], $ident, $line);
                continue;
            }
            if ($ident === $pident) {
                    $block['ident'] = $ident;
                if (preg_match('~^[%#.+\\-@\\~=]{1}~iu',$m[2])) {
                    preg_match('~^([%#.+\\-@a-zA-Z0-9\\~=]+)(.*)$~iu', $m[2], $mm);
                    $block['type'] = 'tag';
                    $block['tagline'] = trim($mm[1]);
                    $block['ctags'] = $this->_parse_tagline($block['tagline']);
                    $block['text'] = '';
                    $block['text'] = trim($mm[2]);
                    $block['attrs'] = array();
                    if ($block['ctags']['attributed']) {
                        $block['attrs'] = $this->_parse_attrs($block['text']);
                    }
                } else {
                    $block['type'] = 'text';
                    $block['text'] = $m[2];
                }
                //echo "== Пихаем ($ident:$pident % $i @ $line) [{$m[2]}]<br/>";
                $pblock[$i++] = $block;
            }
            //print_r($block);
            //print_r($mm);
            $line++;
        }
    }

    public function parse($name) {
        $line = 0;
        $this->_parse_template($name, $this->_blocks[$name], 0, $line);
    }

    private function _parse_attrs($str) {
        $params = array();
        preg_match('~{(.*)}~iu', $str, $params);
        $params = trim($params[1]);
        $params = explode(',', $params);
        $res = array();
        foreach ($params as $param) {
            list($key, $value) = explode('=',$param);
            $res[trim($key)] = trim(trim($value),'\'"');
        }
        return $res;
    }

    public function _parse_tagline($tagline) {
        $tagline = trim($tagline);
        $match = array();
        $res['attributed'] = false;
        $res['evaluate'] = false;
        $res['sanitize'] = false;
        $res['tag'] = self::DEFAULT_TAG;
        $res['classes'] = array();
        $res['id'] = '';
        if (substr($tagline, -1)=='@') { $res['attributed'] = true; $tagline = substr($tagline, 0, -1); }
        if (substr($tagline, -1)=='=') { $res['evaluate'] = true; $tagline = substr($tagline, 0, -1); }
        if (substr($tagline, -1)=='~') { $res['sanitize'] = true; $tagline = substr($tagline, 0, -1); }
        if (substr($tagline, -1)=='&') { $res['sanitize'] = true; $res['evaluate'] = true; $tagline = substr($tagline, 0, -1); }
        preg_match_all('~([.#\\~\\-+!%]{1})(\w+)~iu', $tagline, $match, PREG_SET_ORDER);
        foreach ($match as $tag) {
            $key = trim($tag[1]);
            $value = trim($tag[2]);
            if ($key == '%') {
                $res['tag'] = $value;
            } elseif ($key == '#') {
                $res['id'] = $value;
            } elseif ($key == '.') {
                $res['classes'][] = $value;
            }
        }
        return $res;
    }

    protected function _create_tag($ctag) {
        $attributes = array();
        $res = '';
        if ($ctag['type']=='text') $res = $ctag['text'];
        if ($ctag['type']=='tag') {
            if (!empty ($ctag['ctags']['id'])) { $attributes['id'] = htmlspecialchars($ctag['ctags']['id']); }
            if ($ctag['ctags']['attributed']) { $ctag['text']=''; foreach ($ctag['attrs'] as $attrk => $attr) { $attributes[$attrk] = htmlspecialchars($attr); } }
            if ($this->_config['debug']) $res.=str_pad('',$ctag['ident'] * $this->_config['ident'],' ');
            $res.= '<'.$ctag['ctags']['tag'];
            if (!empty ($ctag['ctags']['classes'])) { $attributes['class'] = implode(' ', $ctag['ctags']['classes']); }
            if (!empty ($attributes)) {
                foreach ($attributes as $key => $value) {
                    $res.= ' '.htmlspecialchars($key).'="'.htmlspecialchars($value).'"';
                }
            }
            $text = ($ctag['ctags']['sanitize'])?htmlspecialchars($ctag['text']):($ctag['text']);
            $res.='>';
            if ($this->_config['debug']) { $res.="\n"; }
            $res.=$text;
            if (isset($ctag['sub'])) { foreach ($ctag['sub'] as $sub) {
                    $res.= $this->_create_tag($sub);
                }
            }
            $res.='</'.$ctag['ctags']['tag'].'>';
            if ($this->_config['debug']) { $res.="\n"; }
        }
        return $res;
    }

    public function generate($name) {
        foreach ($this->_blocks[$name] as $block) {
            echo $this->_create_tag($block);
        }
    }
}
?>